package com.hockeo.tinygds.error;

import java.lang.reflect.Field;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Text;
import com.hockeo.tinygds.TKey;
import com.hockeo.tinygds.annotation.TParent;

/**
 * Indicates that an operation was attempted on a object loaded from the Google Datastore
 * that could not be transformed into a user code instance.
 *
 * @version $Id$
 * @author jjanke
 */
public class TNotLoadableException extends TinyGDSException
{
  private static final long serialVersionUID = 1L;

  /**
   * Creates a new exception. This constructor is private in order to force the user to
   * use one of the static factory methods.
   *
   * @param strMsg the exception message
   */
  private TNotLoadableException( String strMsg )
  {
    super( strMsg );
  }

  public static TNotLoadableException objectHasNoStringId( Key key, Class<?> clsTarget )
  {
    return new TNotLoadableException( "The class '" + clsTarget.getName()
        + "' has no declared String @Id although the entity with the key '" + key.toString() + "' requires one." );
  }

  public static TNotLoadableException objectHasNoLongId( Key key, Class<?> clsTarget )
  {
    return new TNotLoadableException( "The class '" + clsTarget.getName()
        + "' has no declared Long/long @Id although the entity with the key '" + key.toString() + "' requires one." );
  }

  public static TNotLoadableException noStringFieldForTextValue( Text text, Field field )
  {
    return new TNotLoadableException( "The field '" + getFieldName( field ) + "' is not of type String although a '"
        + Text.class.getName() + "' typed value should be set to it." );
  }

  public static TNotLoadableException cannotConvertValue( Object objValue, Field field )
  {
    return new TNotLoadableException( "The value '" + objValue.toString() + "' is of type '"
        + objValue.getClass().getName() + "' and it is not know how to set it to the field '" + getFieldName( field )
        + "' which is of type '" + field.getType().getName() + "'." );
  }

  public static TNotLoadableException noKeyField( Field field )
  {
    return new TNotLoadableException( "The field '" + getFieldName( field ) + "' is expected to be of type '"
        + TKey.class.getName() + "' but is of type '" + field.getType().getName() + "'." );
  }

  public static TNotLoadableException noParentField( Key key, Class<?> clsTarget )
  {
    return new TNotLoadableException( "The entity with the key '" + key.toString() + "' has a parent '"
        + key.getParent().toString() + "' but in the class '" + clsTarget.getName() + "' no field is annotated with @"
        + TParent.class.getName() + "." );
  }

  public static TNotLoadableException missingDiscriminator( Entity ent )
  {
    return new TNotLoadableException( "The entity with the key '" + ent.getKey().toString()
        + "' has no __discriminator__ property." );
  }

  public static TNotLoadableException invalidKind( Entity ent, Class<?> clsExpectedTarget, String strExpectedKind )
  {
    return new TNotLoadableException( "The entity with the key '" + ent.getKey().toString() + "' has the kind '"
        + ent.getKind() + "'. A kind of '" + strExpectedKind
        + "' was expected as it was asked to transform the entity into an object of type '"
        + clsExpectedTarget.getName() + "'." );
  }

  private static String getFieldName( Field field )
  {
    return field.getDeclaringClass().getName() + "#" + field.getName();
  }

}
